#!/bin/bash

#------------------------------------------------------------------------------
# Monitor files inside a directory, running a command (by default make) when a
# file is changed.
#
# Dependencies:
# 	- inotify-tools
# 	- getopt
#------------------------------------------------------------------------------

# We dont use -e, so the program can run if the compilation command fails
set -u -o pipefail

die() {
	echo "compiled: ${@}" && exit 1;
}

usage() {
	cat <<-USAGE_END
		Usage: compiled [OPTION] [FILE]
		Watches FILE(s), executing a command, by default make, on any file
		change. WHen FILE is omitted, watches current directory.

		-c, --command [command]	Execute command on file change instead of make
		-e, --exclude [regex]	Ignore changes produced by files that match regex
		-s, --separate [char]	Draws a separating line, optionally specify line character
		-h, --help		Prints this message
	USAGE_END
	exit ${1}
}

print_separator() {
	local separator=${1}
	for ((i = 0; i < ${COLUMNS}; i++)) ; do 
		printf "${separator}"
	done
}

if [[ $(getopt -T ; echo $?) != 4 ]] ; then
	die "$(cat <<-GETOPT_ERR_MSG_END
		Your getopt is not the enhanced version. Please install a more recent version
		version of getopt.
	GETOPT_ERR_MSG_END
	)"
fi

events='modify,create,delete,move'
exclude_pattern=''
files="${PWD}"
cmd='make'
draw_separator=0
separator='-'

# Call quiet twice to remove needless output
flags="--quiet --quiet --recursive --event ${events}"

short_opts='hc:e:s::'
long_opts='help,command:,exclude:,separate::'
parsed=$(getopt --options ${short_opts} --longoptions ${long_opts} -n 'compiled' -- "${@}")

[[ $? != 0 ]] && die 'Try compiled -h for more information'

eval set -- "${parsed}"

while true; do
	case "${1}" in
		-c|--command) 
			cmd="${2}"
			shift 2
		;;
		-e|--exclude) 
			exclude_pattern="${2}"
			shift 2
		;;
		-s|--separate) 
			[[ -n ${2} ]] && separator=${2}
			draw_separator=1
			shift 2
		;;
		--)
			shift
			break
		;;
		-h|--help) usage 0 ;;
		*) 
			echo 'Internal error.' >&2
			exit 1
		;;
	esac
done

[[ -n "${@}" ]] && files="${@}"
[[ -n ${exclude_pattern} ]] && flags+=" --exclude ${exclude_pattern}"

pid=''
while true ; do
	inotifywait ${flags} ${files}
	sleep 0.1 # Avoid race issues
	[[ -n $pid ]] && kill $pid 2>/dev/null
	echo "${cmd}"
	${cmd}&
	pid=$!
	((draw_separator)) && print_separator ${separator}
done
